;; supercollider/ugen.scm - (c) rohan drape, 2003-2005

;; A <ugen> represents a UGen in a UGen graph.  The string `name'
;; names the C level UGen.  Each value at the list `inputs' is either
;; a <constant>, a <control>, a <ugen> or an <output-proxy>.  Each
;; value at the list `outputs' is a calculation rate indicator
;; <integer> or an <output>.

(define-structure ugen (name
			rate
			inputs
			outputs
			special-index
			index
			id))

;; A <ugen> value is subject to identity preserving transformations
;; during serialization.  A unique <integer> identifier is assigned to
;; each <ugen> at creation and preserved under these operations.

(define generate-ugen-id (identifierN 0))

;; Identifier based equality predicate.

(define (ugen-eq? a b)
  (= (ugen-id a) (ugen-id b)))

;; Specialized constructor.  Elides the index and id fields.

(defineV (make-ugen name
		    rate
		    inputs
		    outputs
		    special-index)
  (make-ugen* name
	      rate
	      inputs
	      outputs
	      special-index
	      #f
	      (generate-ugen-id)))

(define (ugen/inputs u inputs)
  (make-ugen* (ugen-name u)
	      (ugen-rate u)
	      inputs
	      (ugen-outputs u)
	      (ugen-special-index u)
	      (ugen-index u)
	      (ugen-id u)))

(define (ugen/index u index)
  (make-ugen* (ugen-name u)
	      (ugen-rate u)
	      (ugen-inputs u)
	      (ugen-outputs u)
	      (ugen-special-index u)
	      index
	      (ugen-id u)))

;; Read a UGen specification from `port'.  

(define (ugen-read port)
  (let ((name (read-pascal-string port))
	(rate (read-int8 port))
	(number-of-inputs (read-int16 port))
	(number-of-outputs (read-int16 port))
	(special-index (read-int16 port)))
    (let ((inputs (list-read number-of-inputs input-read port))
	  (outputs (list-read number-of-outputs output-read port)))
      (make-ugen name
		 rate
		 inputs
		 outputs
		 special-index))))

;; Write the UGen specification for `u' to `port'.  

(define (ugen-write u port)
  (write-pascal-string (ugen-name u) port)
  (write-int8 (ugen-rate u) port)
  (write-int16 (length (ugen-inputs u)) port)
  (write-int16 (length (ugen-outputs u)) port)
  (write-int16 (ugen-special-index u) port)
  (list-write (ugen-inputs u) input-write port)
  (list-write (ugen-outputs u) output-write port))

(define (ugen-encode u)
  (u8vector-append
   (encode-pascal-string (ugen-name u))
   (encode-int8 (ugen-rate u))
   (encode-int16 (length (ugen-inputs u)))
   (encode-int16 (length (ugen-outputs u)))
   (encode-int16 (ugen-special-index u))
   (u8vector-concatenate (map input-encode (ugen-inputs u)))
   (u8vector-concatenate (map output-encode (ugen-outputs u)))))

;; A UGen user-name is usually just the UGen name with a rate suffix,
;; but for Binary and Unary OpUGens it is the name of the operation
;; without any rate suffix, which is determined by the special index.

(define (ugen-name/user u)
  (let ((name (ugen-name u)))
    (cond ((string= name "BinaryOpUGen")
	   (symbol->string (binop-name (ugen-special-index u))))
	  ((string= name "UnaryOpUGen")
	   (symbol->string (uop-name (ugen-special-index u))))
	  (else 
	   (string-append name (ref '(".ir" ".kr" ".ar" ".dr") (ugen-rate u)))))))

;; Return the `index'th output at `u'.

(define (ugen-output-ref u index)
  (if (>= index (length (ugen-outputs u)))
      (error "ugen-output: illegal index" (structure->constructor u) index)
      (ref (ugen-outputs u) index)))

;; An <input> for a ugen is the duple (ugen-index 0).

(define (ugen->input u)
  (make-input (ugen-index u) 0))

;; Rewrite all inputs as <input> values.

(define (ugen-rewrite/input u)
  (define (rewrite-input i)
    (cond 
     ((constant? i) (constant->input i))
     ((control? i) (control->input i))
     ((ugen? i) (ugen->input i))
     ((output-proxy? i) (output-proxy->input i))
     (else 
      (error "ugen-rewrite/input: illegal input" i))))
  (ugen/inputs u (map rewrite-input (ugen-inputs u))))

